use std::marker::PhantomData;
use std::str::FromStr;

use log::{Level, Log, Metadata, Record};

use crate::appender::Appender;
use crate::core::{self, Message};
use crate::{TargetIndex, TargetMapping};

pub fn name(name: &'static str) -> TargetBuilder {
    TargetBuilder {
        name,
        level: Level::Trace,
        appenders: Vec::default(),
    }
}

pub struct Target {
    name: &'static str,
    level: Level,
    appenders: Vec<Appender>,
}

pub struct TargetBuilder {
    name: &'static str,
    level: Level,
    appenders: Vec<Appender>,
}

impl TargetBuilder {
    pub fn level(mut self, level: &str) -> Result<Self, log::ParseLevelError> {
        self.level = Level::from_str(level)?;
        Ok(self)
    }

    pub fn appender(mut self, appender: Appender) -> Self {
        self.appenders.push(appender);
        self
    }

    pub fn build(self) -> Target {
        Target {
            name: self.name,
            level: self.level,
            appenders: self.appenders,
        }
    }
}

pub struct DefaultMapping {}

impl TargetMapping for DefaultMapping {
    fn map(_: &str) -> crate::TargetIndex {
        crate::TargetIndex::T0
    }
}

pub struct LogBuilder<T: TargetMapping = DefaultMapping> {
    targets: Vec<Target>,
    _marker: PhantomData<T>,
}

impl Default for LogBuilder<DefaultMapping> {
    fn default() -> LogBuilder<DefaultMapping> {
        Self {
            targets: Vec::default(),
            _marker: PhantomData,
        }
    }
}

impl<T: TargetMapping + 'static> LogBuilder<T> {
    pub fn new() -> Self {
        Self {
            targets: Vec::default(),
            _marker: PhantomData,
        }
    }

    pub fn with<F>(self, f: F) -> Self
    where
        F: FnOnce(Self) -> Self,
    {
        f(self)
    }

    pub fn add_logger(mut self, target: Target) -> Self {
        self.targets.push(target);
        self
    }

    pub fn init(self) -> crate::Result<()> {
        let global = &core::LOGGER_GLOBAL;
        let mut logger = Logger::<T>::new();
        let message_sender = global.message_sender.lock()?;
        let mut max_level = Level::Error;
        for target in self.targets.into_iter() {
            if target.level > max_level {
                max_level = target.level;
            }
            let name = T::map(target.name);
            logger.level[name as usize] = target.level;
            for appender in target.appenders.into_iter() {
                message_sender.send(Message::AppenderAdd((name as u8, appender)))?;
            }
        }
        log::set_boxed_logger(Box::new(logger))?;
        log::set_max_level(max_level.to_level_filter());
        Ok(())
    }
}

pub struct Controller<T: TargetMapping = DefaultMapping> {
    _marker: PhantomData<T>,
}

impl Default for Controller<DefaultMapping> {
    fn default() -> Controller<DefaultMapping> {
        Self {
            _marker: PhantomData,
        }
    }
}

impl<T: TargetMapping + 'static> Controller<T> {
    pub fn new() -> Self {
        Self {
            _marker: PhantomData,
        }
    }

    pub fn add_appender(&self, target: &str, appender: Appender) {
        let global = &core::LOGGER_GLOBAL;
        if let Ok(sender) = global.message_sender.lock() {
            if sender
                .send(Message::AppenderAdd((T::map(target) as u8, appender)))
                .is_err()
            {
                println!("logger thread is closed.");
            }
        }
    }

    pub fn remove_appender(&self, target: &str, appender: &str) {
        let global = &core::LOGGER_GLOBAL;
        if let Ok(sender) = global.message_sender.lock() {
            if sender
                .send(Message::AppenderRemove((
                    T::map(target) as u8,
                    appender.to_string(),
                )))
                .is_err()
            {
                println!("logger thread is closed.");
            }
        }
    }
}

struct Logger<T: TargetMapping> {
    level: [Level; TargetIndex::Max as usize + 1],
    _marker: PhantomData<T>,
}

impl<T: TargetMapping> Logger<T> {
    fn new() -> Self {
        Self {
            level: [Level::Error; TargetIndex::Max as usize + 1],
            _marker: PhantomData,
        }
    }
}

impl<T: TargetMapping> Log for Logger<T> {
    fn enabled(&self, metadata: &Metadata) -> bool {
        metadata.level() <= self.level[T::map(metadata.target()) as usize]
    }

    fn log(&self, record: &Record) {
        if self.enabled(record.metadata()) {
            core::record_log::<T>(record);
        }
    }

    fn flush(&self) {
        close();
    }
}

fn close() {
    println!("logger close...");
    let global = &core::LOGGER_GLOBAL;
    if let Ok(sender) = global.message_sender.lock() {
        if sender.send(Message::Terminate).is_err() {
            println!("logger thread already closed.");
        }
    }
    if let Some(thread) = core::LOGGER_GLOBAL
        .thread_join_handle
        .lock()
        .ok()
        .and_then(|mut handle| handle.take())
    {
        match thread.join() {
            Ok(_) => {}
            Err(_) => println!("logger thread join failed."),
        }
    }
    println!("logger worker closed success");
}
